home *** CD-ROM | disk | FTP | other *** search
/ Aminet 8 / Aminet 8 (1995)(GTI - Schatztruhe)[!][Oct 1995].iso / Aminet / comm / tcp / PutMailSRC.lha / PutMail.c < prev    next >
C/C++ Source or Header  |  1995-06-08  |  19KB  |  855 lines

  1. /*
  2.  *    SMTP Send Mail Utility.
  3.  */
  4.  
  5. #define __Program__ "PutMail"
  6. #define __Version__ "1.23"
  7.  
  8. #include <Defaults.h>
  9. #include <SMTP.h>
  10.  
  11. #include <proto/dos.h>
  12. #include <proto/exec.h>
  13. #include <proto/intuition.h>
  14.  
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <strings.h>
  18. #include <sys/dir.h>
  19. #include <dos/stdio.h>
  20. #include <dos/dosextens.h>
  21. #include <exec/memory.h>
  22.  
  23. #define AMITCP_NEW_NAMES
  24. #define _OPTINLINE
  25. #include <LocalInclude:AmiTCP/errno.h>
  26. #include <netdb.h>
  27.  
  28. BPTR fhIn = NULL, fhOut = NULL;
  29. struct Remember *MemList = NULL;
  30. BPTR Console = NULL, MyConsole = NULL;
  31.  
  32. #define OPT_TEMPLATE "MAIL=MAILFILE/A,TO=RECIPIENT/K/M,SUB=SUBJECT/K,HOME/K,REPLYTO/K,SMTPHOST/K,NOHEAD/S,NOADDR/S,NOSIG/S,VERBOSE/S"
  33. #define OPT_MAIL 0
  34. #define OPT_TO 1
  35. #define OPT_SUB 2
  36. #define OPT_HOME 3
  37. #define OPT_REPLYTO 4
  38. #define OPT_SMTPHOST 5
  39. #define OPT_NOHEAD 6
  40. #define OPT_NOADDR 7
  41. #define OPT_NOSIG 8
  42. #define OPT_VERBOSE 9
  43. #define OPT_COUNT 10
  44.  
  45. struct RDArgs *Args;
  46. LONG Options[OPT_COUNT];
  47.  
  48. STRPTR Mail;               // Mail file name.
  49. STRPTR *To;                // TO Address.
  50. STRPTR Subject;            // Subject text.
  51. STRPTR HomeDir;            // Home directory.
  52. STRPTR ReplyTo;             // E-Mail address to send replies to.
  53. STRPTR SMTPHost;           // SMTP Mail Host.
  54.  
  55. BOOL NoHead;
  56. BOOL NoAddr;
  57. BOOL NoSig;
  58. BOOL Verbose;
  59.  
  60. STRPTR Domain;
  61. #define USERID_LEN 16
  62. char UserID[USERID_LEN];
  63. #define USERNAME_LEN 64
  64. char UserName[USERNAME_LEN];
  65. #define HOSTNAME_LEN 256
  66. char HostName[HOSTNAME_LEN];
  67. char MailTemp[MAXNAMLEN + 1];
  68.  
  69. #define CMDBUF_SIZE 512
  70. #define DATBUF_SIZE 1024
  71.  
  72. int hSocket = SMTP_NO_SOCKET;
  73. struct sockaddr_in INetSocketAddr;
  74.  
  75.  
  76. void __regargs Abort(STRPTR ErrorMsg, BOOL ErrorCR, int ErrorRC, LONG Error2);
  77.  
  78.  
  79. void __regargs Message(STRPTR Msg, BOOL CR)
  80. {
  81.  
  82. if (!Console)
  83.  
  84.    {
  85.    if (Console = Output())
  86.       MyConsole = NULL;
  87.    else
  88.       Console = MyConsole = Open(STDIO, MODE_READWRITE);
  89.    
  90.    Write(Console, "\n", 1);
  91.    Message(Version, TRUE);
  92.    Message("Written by Martin V Lanza.\n", TRUE);
  93.    }
  94.  
  95. if (Msg) Write(Console, Msg, strlen(Msg));
  96. if (CR) Write(Console, "\n", 1);
  97.  
  98. return;
  99. }
  100.  
  101.  
  102. void __regargs VerboseMsg(STRPTR Func, STRPTR Name, STRPTR Value)
  103. {
  104. static BOOL DoLF;
  105. static char Msg[DATBUF_SIZE * 2];
  106.  
  107. if (!Verbose) return;
  108.  
  109. sprintf(Msg, "%-16s  ", Func);
  110.  
  111. if (Name) strcat(Msg, Name);
  112. if (Value) strcat(Msg, Value);
  113.  
  114. if (Msg[strlen(Msg)-1] == '\n')
  115.    DoLF = FALSE;
  116. else
  117.    DoLF = TRUE; 
  118.  
  119. Message(Msg, DoLF);
  120.  
  121. return;
  122. }
  123.  
  124.  
  125. BOOL __regargs RecvDat(STRPTR DatBuf, int *DatLen)
  126. {
  127. static char Func[] = "RecvDat";
  128.  
  129. errno = 0;
  130. if (hSocket == SMTP_NO_SOCKET) return(FALSE);
  131. *DatLen = Recv(hSocket, DatBuf, DATBUF_SIZE - 1, 0);
  132. if (*DatLen) DatBuf[*DatLen] = '\0'; else DatBuf[0] = '\0';
  133. VerboseMsg(Func, "Recv() ", DatBuf);
  134.  
  135. return(TRUE);
  136. }
  137.  
  138.  
  139. BOOL __regargs SendDat(STRPTR SendData)
  140. {
  141. static char Func[] = "SendDat";
  142. static char StrBuf[DATBUF_SIZE];
  143.  
  144. if (hSocket == SMTP_NO_SOCKET) return(FALSE);
  145. if (!SendData) return(TRUE);
  146.  
  147. // Conform to RFC821.
  148. // This prevents premature message termination by user text.
  149.  
  150. *StrBuf = '\0';
  151. if (SendData[0] == '.')
  152.    if (SendData[1] == '\r')
  153.       if (SendData[2] == '\n')
  154.             {
  155.             StrBuf[0] = '.';
  156.             StrBuf[1] = '\0';
  157.             }
  158.  
  159. strcat(StrBuf, SendData);
  160. VerboseMsg(Func, "Send() ", StrBuf);
  161. if (Send(hSocket, StrBuf, strlen(StrBuf), 0) != -1) return(TRUE);
  162. Abort("Socket message send failure.", TRUE, RETURN_FAIL, Errno());
  163. return(FALSE);
  164. }
  165.  
  166.  
  167. void __regargs SendCmd(STRPTR CmdText, STRPTR ParmText)
  168. {
  169. static int StrLen;
  170. static char StrBuf[DATBUF_SIZE];
  171.  
  172. if (hSocket == SMTP_NO_SOCKET) return;
  173.  
  174. if (!CmdText || !*CmdText)
  175.    *StrBuf = '\0';
  176. else
  177.    if (!ParmText || !*ParmText)
  178.       sprintf(StrBuf, "%s\r\n", CmdText);
  179.    else
  180.       sprintf(StrBuf, "%s %s\r\n", CmdText, ParmText);
  181.  
  182. if (!SendDat(StrBuf)) return;
  183. if (!RecvDat(StrBuf, &StrLen)) return;
  184.  
  185. switch (atoi(StrBuf))
  186.    {
  187.    case SMTP_STATUS_REPLY :   break;
  188.    case SMTP_HELP_MESSAGE :   break;
  189.    case SMTP_SERVICE_READY:   break;
  190.    case SMTP_SERVICE_CLOSE:   break;
  191.    case SMTP_OK           :   break;
  192.    case SMTP_OK_FORWARD   :   break;
  193.    case SMTP_SEND_MAIL    :   break;
  194.    
  195.    default:
  196.       {
  197.       char ErrorMsg[256];
  198.       sprintf(ErrorMsg, "Bad %s command response: %s", CmdText, StrBuf);
  199.       Abort(ErrorMsg, TRUE, RETURN_FAIL, ERROR_BAD_NUMBER);
  200.       }
  201.    }
  202.  
  203. // Extended responce may not fit in the buffer :-(
  204. // Keep reading untill we get it all.
  205. if (StrBuf[3] == '-')
  206.    while (StrBuf[StrLen - 1] != '\n')
  207.       if (!RecvDat(StrBuf, &StrLen)) break;
  208.          
  209. return;
  210. }
  211.  
  212.  
  213. void __regargs SocketDisconnect(void)
  214. {
  215. char Func[] = "SocketDisconnect";
  216.  
  217. if (hSocket != SMTP_NO_SOCKET)
  218.    {
  219.    SendCmd("QUIT", NULL);
  220.    Shutdown(hSocket, 2);
  221.    CloseSocket(hSocket);
  222.    hSocket = SMTP_NO_SOCKET;
  223.    VerboseMsg(Func, "Disconnected from SMTP Host ", SMTPHost);
  224.    }
  225.  
  226. return;
  227. }
  228.  
  229.  
  230. void __regargs CleanUp(void)
  231. {
  232. char Func[] = "CleanUp";
  233.  
  234. VerboseMsg(Func, "Cleanup in progress.", NULL);
  235.  
  236. if (fhIn) Close(fhIn);
  237.  
  238. if (fhOut) Close(fhOut);
  239.  
  240. if (Args) FreeArgs(Args);
  241.  
  242. if (MemList) FreeRemember(&MemList, TRUE);
  243.  
  244. if (MyConsole)
  245.    {
  246.    Message("\nPress RETURN to terminate program: ", FALSE);
  247.    FGetC(MyConsole);
  248.    Close(MyConsole);
  249.    }
  250.    
  251. return;
  252. }
  253.  
  254.  
  255. void __regargs Abort(STRPTR ErrorMsg, BOOL ErrorCR, int ErrorRC, LONG Error2)
  256. {
  257. static BOOL Aborting;
  258.  
  259. if (Aborting) return;
  260. Aborting = TRUE;
  261.  
  262. if (ErrorMsg)
  263.    {
  264.    Message(__Program__ ": ", FALSE);
  265.    Message(ErrorMsg, ErrorCR);
  266.    }
  267.  
  268. SendCmd("RSET", NULL);
  269. SocketDisconnect();
  270.  
  271. CleanUp();
  272.  
  273. if (Error2) ((struct Process *) FindTask(NULL))->pr_Result2 = Error2;
  274.  
  275. exit(ErrorRC);
  276. }
  277.  
  278.  
  279. void __regargs SocketConnect(void)
  280. {
  281. struct hostent *HostAddr;
  282. char Func[] = "SocketConnect";
  283.  
  284. HostAddr = GetHostByName(SMTPHost);
  285. if (!HostAddr)
  286.    {
  287.    VerboseMsg(Func, "GetHostByName() ", SMTPHost);
  288.    if (h_errno == TRY_AGAIN)
  289.       Abort("Unable to locate the SMTP Host, try again later.", TRUE, RETURN_FAIL, h_errno);
  290.    else
  291.       Abort("The SMTP Host is invalid.", TRUE, RETURN_FAIL, h_errno);
  292.    }
  293.  
  294. INetSocketAddr.sin_len = sizeof(INetSocketAddr);
  295. INetSocketAddr.sin_family = AF_INET;
  296. INetSocketAddr.sin_port = 25; // SMTP
  297. INetSocketAddr.sin_addr.s_addr = 0;
  298.  
  299. memcpy(&INetSocketAddr.sin_addr, HostAddr->h_addr, HostAddr->h_length);
  300.  
  301. hSocket = Socket(HostAddr->h_addrtype, SOCK_STREAM, 0);
  302. if (hSocket == SMTP_NO_SOCKET)
  303.    {
  304.    VerboseMsg(Func, "Socket() Failed!", NULL);
  305.    Abort("Socket allocation failed.", TRUE, RETURN_FAIL, Errno());
  306.    }
  307.  
  308. if (Connect(hSocket, (struct sockaddr *) &INetSocketAddr, sizeof(INetSocketAddr)) == -1)
  309.    {
  310.    VerboseMsg(Func, "Connect() Failed!", NULL);
  311.    Abort("Socket connection failed.", TRUE, RETURN_FAIL, Errno());
  312.    }
  313.  
  314. VerboseMsg(Func, "Connected to SMTP Host ", SMTPHost);
  315.  
  316. SendCmd(NULL, NULL);       // Check for the Welcome... message.
  317. SendCmd("HELO", HostName); // Say HELLO, Receive HELLO.
  318.    
  319. return;
  320. }
  321.  
  322.  
  323. BOOL __regargs GetEnvVar(STRPTR Name, STRPTR Buffer, long Len)
  324. {
  325. static int BufLen, EnvLen;
  326.  
  327. BufLen = GetVar(Name, Buffer, Len - 1, GVF_GLOBAL_ONLY);
  328. EnvLen = IoErr();
  329. SetIoErr(NULL);
  330.  
  331. if (BufLen <= NULL) return(FALSE);
  332. if (BufLen != EnvLen) return(FALSE);
  333.  
  334. return(TRUE);
  335. }
  336.  
  337.  
  338. BOOL __regargs GetEMailAddr(void)
  339. {
  340. int Len;
  341. BPTR fhFile;
  342. char strName[MAXNAMLEN + 1];
  343. char Func[] = "GetEMailAddr";
  344.  
  345. strcpy(strName, HomeDir);
  346. AddPart(strName, ".EMailAddr", MAXNAMLEN);
  347.  
  348. if (!(fhFile = Open(strName, MODE_OLDFILE)))
  349.    {
  350.    VerboseMsg(Func, strName, " could not be opened.");
  351.    return(FALSE);
  352.    }
  353.  
  354. VerboseMsg(Func, strName, " successfully opened.");
  355. FGets(fhFile, ReplyTo, USERID_LEN + HOSTNAME_LEN);
  356.  
  357. if (IoErr())
  358.    {
  359.    VerboseMsg(Func, "Read failure on ", strName);
  360.    Close(fhFile);
  361.    return(FALSE);
  362.    }
  363.  
  364. Close(fhFile);
  365.  
  366. Len = strlen(ReplyTo);
  367. if (!Len) return(FALSE);
  368.  
  369. while (--Len)
  370.    {
  371.    if (ReplyTo[Len] != ' ')
  372.       if (ReplyTo[Len] != '\r')
  373.          if (ReplyTo[Len] != '\n')
  374.             break;
  375.             
  376.    ReplyTo[Len] = '\0';
  377.    }
  378.  
  379. return(TRUE);
  380. }
  381.  
  382.  
  383. void __regargs InitProg(void)
  384. {
  385. struct passwd *UserList;
  386. char Func[] = "InitProg";
  387. struct DateStamp TheDateStamp;
  388.  
  389. memset(Options, NULL, sizeof(LONG) * OPT_COUNT);
  390. Args = ReadArgs(OPT_TEMPLATE, Options, NULL);
  391. if (!Args)
  392.    {
  393.    PrintFault(IoErr(), NULL);
  394.     Abort("Argument Template is " OPT_TEMPLATE, TRUE, RETURN_FAIL, ERROR_BAD_TEMPLATE);
  395.    }
  396.  
  397. Mail = (STRPTR) Options[OPT_MAIL];
  398. To = (STRPTR *) Options[OPT_TO];
  399. Subject = (STRPTR) Options[OPT_SUB];
  400. HomeDir = (STRPTR) Options[OPT_HOME];
  401. ReplyTo = (STRPTR) Options[OPT_REPLYTO];
  402. SMTPHost = (STRPTR) Options[OPT_SMTPHOST];
  403. NoHead = Options[OPT_NOHEAD];
  404. NoAddr = Options[OPT_NOADDR];
  405. NoSig = Options[OPT_NOSIG];
  406. Verbose = Options[OPT_VERBOSE];
  407.  
  408. DateStamp(&TheDateStamp);
  409. sprintf(MailTemp, "T:PutMail.%ld@%ld", FindTask(NULL), TheDateStamp.ds_Minute);
  410.  
  411. if (!GetEnvVar("USER", UserID, USERID_LEN))
  412.    Abort("ENV variable 'USER' not found.", TRUE, RETURN_FAIL, NULL);
  413.    
  414. if ((UserList = getpwnam(UserID)))
  415.    strcpy(UserName, UserList->pw_gecos);
  416. else
  417.    *UserName = '\0';
  418.  
  419. if (!HomeDir)
  420.    {
  421.    HomeDir = AllocRemember(&MemList, MAXNAMLEN + 1, MEMF_ANY | MEMF_CLEAR);
  422.    if (!HomeDir) Abort("Not enough free memory.", TRUE, RETURN_FAIL, ERROR_NO_FREE_STORE);
  423.    if (!GetEnvVar("HOME", HomeDir, MAXNAMLEN))
  424.       Abort("'HOME' not found.", TRUE, RETURN_FAIL, NULL);
  425.    }
  426.    
  427. if (!GetEnvVar("HOSTNAME", HostName, HOSTNAME_LEN))
  428.    Abort("ENV variable 'HOSTNAME' not found.", TRUE, RETURN_FAIL, NULL);
  429.  
  430. Domain = HostName;
  431. while (*Domain && *Domain != '.') Domain++;
  432. if (*Domain) Domain++;
  433.  
  434. if (!SMTPHost)
  435.    {
  436.    SMTPHost = AllocRemember(&MemList, HOSTNAME_LEN, MEMF_ANY | MEMF_CLEAR);
  437.    if (!SMTPHost) Abort("Not enough free memory.", TRUE, RETURN_FAIL, ERROR_NO_FREE_STORE);
  438.    if (!GetEnvVar("SMTPHOST", SMTPHost, HOSTNAME_LEN))
  439.       {
  440.       struct hostent *HostAddr;
  441.       if (HostAddr = GetHostByName("SMTPHOST"))
  442.          strcpy(SMTPHost, HostAddr->h_name);
  443.       else
  444.          {
  445.          strcpy(SMTPHost, Domain);
  446.          VerboseMsg(Func, "SMTPHost name not provided, assumed ", SMTPHost);
  447.          }
  448.       }
  449.    }
  450.       
  451. if (!ReplyTo)
  452.    {
  453.    ReplyTo = AllocRemember(&MemList, USERID_LEN + HOSTNAME_LEN, MEMF_ANY | MEMF_CLEAR);
  454.    if (!ReplyTo) Abort("Not enough free memory.", TRUE, RETURN_FAIL, ERROR_NO_FREE_STORE);
  455.    if (!GetEnvVar("EMAILADDR", ReplyTo, USERID_LEN + HOSTNAME_LEN))
  456.       if (!GetEMailAddr())
  457.          {
  458.          sprintf(ReplyTo, "%s@%s", UserID, SMTPHost);
  459.          VerboseMsg(Func, "ReplyTo & EMAILADDR not provided, assumed ", ReplyTo);
  460.          }
  461.    }
  462.  
  463. if (Verbose)
  464.    {
  465.    VerboseMsg(Func, "MailFile=", Mail);
  466.    if (Subject) VerboseMsg(Func, "Subject=", Subject);
  467.    VerboseMsg(Func, "Home=", HomeDir);
  468.    VerboseMsg(Func, "ReplyTo=", ReplyTo);
  469.    VerboseMsg(Func, "SMTPHost=", SMTPHost);
  470.    NoHead ? VerboseMsg(Func, "NoHead=", "TRUE") : VerboseMsg(Func, "NoHead=", "FALSE");
  471.    NoAddr ? VerboseMsg(Func, "NoAddr=", "TRUE") : VerboseMsg(Func, "NoAddr=", "FALSE");
  472.    NoSig ? VerboseMsg(Func, "NoSig=", "TRUE") : VerboseMsg(Func, "NoSig=", "FALSE");
  473.    Verbose ? VerboseMsg(Func, "Verbose=", "TRUE") : VerboseMsg(Func, "Verbose=", "FALSE");
  474.    }
  475.  
  476. return;
  477. }
  478.  
  479.  
  480. void __regargs CopyFile(int NoBlankLines, STRPTR ErrText)
  481. {
  482. static char StrBuf[DATBUF_SIZE];
  483.  
  484. while(FGets(fhIn, StrBuf, DATBUF_SIZE))
  485.    {
  486.    int register i;
  487.    i = strlen(StrBuf) - 1;
  488.  
  489.    //Strip CR & LF to avoid Amiga/IBM differences.
  490.    if (i >= 0 && StrBuf[i] == '\n') StrBuf[i--] = '\0';
  491.    if (i >= 0 && StrBuf[i] == '\r') StrBuf[i--] = '\0';
  492.  
  493.    while (i >= 0 && StrBuf[i] == ' ') i--;
  494.    if (i < 0 && NoBlankLines) continue;
  495.  
  496.    //Put standard CR LF on end of line.
  497.    StrBuf[++i] = '\r';
  498.    StrBuf[++i] = '\n';
  499.    StrBuf[++i] = '\0';
  500.  
  501.    if (FPuts(fhOut, StrBuf))
  502.       if (IoErr()) Abort("Write Error on temporary mail file.", TRUE, RETURN_FAIL, NULL);
  503.    }
  504.  
  505. if (IoErr()) Abort(ErrText, TRUE, RETURN_FAIL, NULL);
  506.  
  507. Close(fhIn);
  508. fhIn = NULL;
  509.  
  510. return;
  511. }
  512.  
  513.  
  514. BPTR __regargs OpenFile(STRPTR Path, STRPTR Name, LONG Mode)
  515. {
  516. static BPTR fhFile;
  517. static char strMode[33];
  518. static char strName[MAXNAMLEN + 1];
  519. static char Func[] = "OpenFile";
  520.  
  521. switch (Mode)
  522.    {
  523.    case MODE_OLDFILE:
  524.       strcpy(strMode, " opened for Input."); break;
  525.    case MODE_NEWFILE:
  526.       strcpy(strMode, " opened for Output.");   break;      
  527.    case MODE_READWRITE:
  528.       strcpy(strMode, " opened for Input & Output."); break;      
  529.    }
  530.  
  531. strcpy(strName, Path);
  532. AddPart(strName, Name, MAXNAMLEN);
  533. fhFile = Open(strName, Mode);
  534. if (fhFile)
  535.    {
  536.    VerboseMsg(Func, strName, strMode);
  537.    if (!SetVBuf(fhFile, NULL, BUF_FULL, 4096))
  538.       VerboseMsg(Func, strName, " will be buffered.");
  539.    }
  540. else
  541.    VerboseMsg(Func, "Unable to open ", strName);
  542.    
  543. return(fhFile);
  544. }
  545.  
  546.  
  547. void __regargs CreateMail(void)
  548. {
  549. char Advert[DATBUF_SIZE];
  550. char StrBuf[DATBUF_SIZE];
  551. char StrDay[LEN_DATSTRING];
  552. char StrDate[LEN_DATSTRING];
  553. char StrTime[LEN_DATSTRING];
  554. struct DateTime TheDateTime;
  555.  
  556.  
  557. // Advertisement :-)
  558. sprintf(Advert, "X-SMTP-Client: Amiga %s %s by Martin Lanza.\r\n", __Program__, __Version__);
  559.    
  560. if (!(fhOut = OpenFile("", MailTemp, MODE_NEWFILE)))
  561.    Abort("Unable to create temporary mail file.", TRUE, RETURN_FAIL, IoErr());
  562.  
  563. if (NoHead && NoAddr) FPuts(fhOut, Advert);
  564.  
  565. if (!NoHead)
  566.    {
  567.    if (fhIn = OpenFile(HomeDir, ".header", MODE_OLDFILE))
  568.       CopyFile(TRUE, "Error reading '.header' file.");
  569.    if (NoAddr) FPuts(fhOut, Advert);
  570.    }
  571.  
  572.    
  573. if (!NoAddr)
  574.    {
  575.    // From:
  576.    if (*UserName)
  577.       sprintf(StrBuf, "From: %s@%s (%s)\r\n", UserID, HostName, UserName);
  578.    else
  579.       sprintf(StrBuf, "From: %s@%s\r\n", UserID, HostName);
  580.  
  581.    FPuts(fhOut, StrBuf);
  582.  
  583.    // X-SMTP-Client:
  584.    FPuts(fhOut, Advert);
  585.              
  586.    // Date:
  587.    DateStamp(&TheDateTime.dat_Stamp);
  588.    TheDateTime.dat_Format = FORMAT_DOS;
  589.    TheDateTime.dat_Flags = NULL;
  590.    TheDateTime.dat_StrDay = StrDay;
  591.    TheDateTime.dat_StrDate = StrDate;
  592.    TheDateTime.dat_StrTime = StrTime;
  593.    DateToStr(&TheDateTime);
  594.    StrDate[2] = ' '; StrDate[6] = ' ';
  595.    sprintf(StrBuf, "Date: %3.3s, %s %s\r\n", StrDay, StrDate, StrTime);
  596.    FPuts(fhOut, StrBuf);
  597.              
  598.    // Reply-To:
  599.    if (ReplyTo)
  600.       {
  601.       sprintf(StrBuf, "Reply-To: %s\r\n", ReplyTo);
  602.       FPuts(fhOut, StrBuf);
  603.       }
  604.  
  605.    // To:
  606.    if (To)
  607.       {
  608.       int i;
  609.       char Desc[] = "To:";
  610.       
  611.       for (i=0; To[i]; i++)
  612.          {
  613.          sprintf(StrBuf, "%3.3s %s \r\n", Desc, To[i]);
  614.          if (To[i+1]) StrBuf[strlen(StrBuf) - 3] = ',';
  615.          strcpy(Desc, "   ");
  616.          FPuts(fhOut, StrBuf);
  617.          }
  618.       }
  619.                        
  620.    // Subject:
  621.    if (Subject)
  622.       {
  623.       sprintf(StrBuf, "Subject: %s\r\n", Subject);
  624.       FPuts(fhOut, StrBuf);
  625.       }
  626.    
  627.    if (To)
  628.       if(To[0])   
  629.          {
  630.          sprintf(StrBuf, "\r\n");
  631.          FPuts(fhOut, StrBuf);
  632.          }
  633.    }
  634.  
  635. if (Mail)
  636.    if (fhIn = OpenFile("", Mail, MODE_OLDFILE))
  637.       CopyFile(FALSE, "Error reading Mail file.");
  638.    else
  639.       Abort("Unable to process Mail input file.", TRUE, RETURN_FAIL, IoErr());
  640.  
  641. if (!NoSig)
  642.    if (fhIn = OpenFile(HomeDir, ".signature", MODE_OLDFILE))
  643.       CopyFile(FALSE, "Error reading '.signature' file.");
  644.  
  645. Close(fhOut);
  646. fhOut = NULL;
  647.  
  648. return;
  649. }
  650.  
  651.  
  652. void __regargs FilteredAppend(STRPTR Srce, STRPTR Dest)
  653. {
  654. int register i=0, j=0;
  655. int max = DATBUF_SIZE * 2;
  656.  
  657. // Find end of Dest.
  658. while(Dest[j]) j++;
  659.  
  660. while(Srce[i])
  661.    {
  662.    
  663.    // Filter these.
  664.    if (Srce[i] != '\t')
  665.       if (Srce[i] != '\r')
  666.          if (Srce[i] != '\n')
  667.             // Copy char.
  668.             Dest[j++] = Srce[i];
  669.  
  670.    i++;
  671.    
  672.    if (j >= max)
  673.       Abort("'To:' string exceeds buffer size or is illegal.", TRUE, RETURN_FAIL, NULL);
  674.    }
  675.  
  676. Dest[j] = '\0';
  677.  
  678. return;
  679. }
  680.  
  681.  
  682. void __regargs LocateTo(void)
  683. {
  684. int i;
  685. STRPTR ToData;
  686. STRPTR *ToArray;
  687. char Desc[] = "To: ";
  688. char StrBuf[DATBUF_SIZE];
  689. char Func[] = "LocateTo";
  690.  
  691. if (To)
  692.    if (To[0])
  693.       return;
  694.  
  695. ToData = AllocRemember(&MemList, DATBUF_SIZE * 2, MEMF_ANY | MEMF_CLEAR);
  696. if (!ToData) Abort("Not enough free memory.", TRUE, RETURN_FAIL, ERROR_NO_FREE_STORE);
  697. ToArray = AllocRemember(&MemList, sizeof(STRPTR) * 64, MEMF_ANY | MEMF_CLEAR);
  698. if (!ToArray) Abort("Not enough free memory.", TRUE, RETURN_FAIL, ERROR_NO_FREE_STORE);
  699.  
  700. if (!(fhIn = OpenFile("", MailTemp, MODE_OLDFILE)))
  701.    Abort("Unable to open temporary mail file.", TRUE, RETURN_FAIL, IoErr());
  702.    
  703. VerboseMsg(Func, "Scanning temporary mail file for 'To:'.", NULL);
  704.  
  705. while(FGets(fhIn, StrBuf, DATBUF_SIZE))
  706.       {
  707.       if (*StrBuf == '\r')
  708.          // This must mean we are past the message header.
  709.          Abort("Unable to locate 'To:' address.", TRUE, RETURN_FAIL, NULL);
  710.  
  711.       if (!strnicmp(StrBuf, "To: ", 4)) break;
  712.       }
  713.  
  714. if (IoErr()) Abort("Read Error on temporary mail file.", TRUE, RETURN_FAIL, IoErr());
  715.  
  716. FilteredAppend(&StrBuf[4], ToData);
  717.  
  718. while (FGets(fhIn, StrBuf, DATBUF_SIZE))
  719.    {
  720.    if (StrBuf[0] != ' ' && StrBuf[0] != '\t') break;
  721.    FilteredAppend(StrBuf, ToData);
  722.    }
  723.                      
  724. if (IoErr()) Abort("Read Error on temporary mail file.", TRUE, RETURN_FAIL, IoErr());
  725.  
  726. Close(fhIn);
  727. fhIn = NULL;
  728.  
  729. i=0;
  730. ToArray[i++] = ToData;
  731. while(*ToData && i < 63)
  732.    {
  733.    if (*ToData == ',')
  734.       {
  735.       *ToData = '\0';
  736.       ToArray[i++] = ToData + 1;
  737.       }
  738.  
  739.    ToData++;
  740.    }
  741.  
  742. To = ToArray;
  743.  
  744. for (i=0; To[i]; i++)
  745.    {
  746.    // Ignore leading spaces.
  747.    while(To[i] && *To[i] == ' ') To[i]++;
  748.  
  749.    VerboseMsg(Func, Desc, To[i]);
  750.    strcpy(Desc, " &  ");
  751.    }
  752.    
  753. return;
  754. }
  755.  
  756.  
  757. void __regargs MailInit(void)
  758. {
  759. int i;
  760. STRPTR ToAddr, RouteAddr[2];
  761. char ToStr[USERID_LEN + HOSTNAME_LEN + 10];
  762. char FromStr[USERID_LEN + HOSTNAME_LEN + 10];
  763.  
  764. sprintf(FromStr, "FROM:<%s@%s>", UserID, HostName);
  765. SendCmd("MAIL", FromStr);
  766.  
  767. for (i = 0; To[i]; i++)
  768.    {
  769.    RouteAddr[0] = strchr(To[i], '<');
  770.    RouteAddr[1] = strchr(To[i], '>');
  771.    if (RouteAddr[0] && RouteAddr[1])
  772.       {
  773.       strcpy(ToStr, "To:");
  774.       strncat(ToStr, RouteAddr[0], 1 + RouteAddr[1] - RouteAddr[0]);
  775.       }
  776.    else
  777.       {
  778.       // Ignore leading spaces.
  779.       while(To[i] && *To[i] == ' ') To[i]++;
  780.       
  781.       // Ignore trailing spaces.
  782.       ToAddr = To[i] + strlen(To[i]) - 1;
  783.       while (*ToAddr == ' ' && ToAddr > To[i]) 
  784.          *(ToAddr--) = '\0';
  785.             
  786.       sprintf(ToStr, "To:<%s>", To[i]);
  787.       }
  788.       
  789.    SendCmd("RCPT", ToStr);
  790.    }
  791.  
  792. SendCmd("DATA", NULL);
  793.  
  794. return;
  795. }
  796.  
  797.  
  798. void __regargs MailSend(void)
  799. {
  800. char strBuf[DATBUF_SIZE];
  801.  
  802. if (!(fhIn = OpenFile("", MailTemp, MODE_OLDFILE)))
  803.    Abort("Unable to open temporary mail file.", TRUE, RETURN_FAIL, IoErr());
  804.    
  805. while(FGets(fhIn, strBuf, DATBUF_SIZE)) SendDat(strBuf);
  806.  
  807. if (IoErr()) Abort("Read Error on temporary mail file.", TRUE, RETURN_FAIL, IoErr());
  808.  
  809. Close(fhIn);
  810. fhIn = NULL;
  811.  
  812. return;
  813. }
  814.  
  815.  
  816. void __regargs MailEnd(void)
  817. {
  818. char Func[] = "MailEnd";
  819.  
  820. SendCmd("\r\n.", NULL);
  821.  
  822. VerboseMsg(Func, "Mail successfully sent.", NULL);
  823.  
  824. return;
  825. }
  826.  
  827.  
  828. int __regargs main(int argc, char **argv)
  829. {
  830.  
  831. if (!argc) exit(RETURN_FAIL);
  832.  
  833. InitProg();
  834.  
  835. CreateMail();
  836.  
  837. LocateTo();
  838.  
  839. SocketConnect();
  840.  
  841. MailInit();
  842.  
  843. MailSend();
  844.  
  845. MailEnd();
  846.  
  847. SocketDisconnect();
  848.  
  849. CleanUp();
  850.  
  851. DeleteFile(MailTemp);
  852.  
  853. return(RETURN_OK);
  854. }
  855.